﻿using System;
using System.Globalization;
using Notoric.Commanding.CommandBase;
using Notoric.Model.Elements;
using System.ComponentModel;

namespace Notoric.ViewModel.Elements
{
    /// <summary>
    /// Adapts a <see cref="SectionElement"/>.
    /// </summary>
    public abstract class Section : PositionableElement
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Section"/> instance.
        /// </summary>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="SectionElement"/>
        /// </para>
        /// <para>
        /// The model section element adapted by this view-model section instance.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="ViewModelElement"/>
        /// </para>
        /// <para>
        /// The element that directly owns this section.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        protected Section(SectionElement adaptedElement, ViewModelElement owner)
            : base(adaptedElement, owner, owner.UndoStack, owner.RedoStack, false)
        {
            if (adaptedElement.Interior is Model.Elements.CWNotation.MusicSectionInterior)
                interior = new CWNotation.MusicSectionInterior(this as MusicSection, adaptedElement.Interior as Model.Elements.CWNotation.MusicSectionInterior);
            else if (adaptedElement.Interior is Model.Elements.DefaultImageSectionInterior)
                interior = new DefaultImageSectionInterior(adaptedElement.Interior as Model.Elements.DefaultImageSectionInterior, this as ImageSection);
            else if (adaptedElement.Interior is Model.Elements.RtfTextSectionInteriorElement)
                interior = new RtfTextSectionInterior(adaptedElement.Interior as Model.Elements.RtfTextSectionInteriorElement, this as TextSection);

            interior.PropertyChanged += new PropertyChangedEventHandler(interior_PropertyChanged);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the PropertyChanged event of the section interior.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void interior_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsContentSelected")
                RaisePropertyChanged("IsContentSelected");
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Clears the selection in this section.
        /// </summary>
        public void ClearSelection()
        {
            Interior.ClearSelection();
        }

        /// <summary>
        /// Creates an undoable command that sets the rotation angle of the section.
        /// </summary>
        /// <param name="rotationAngle">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The angle, in degrees, of a clock-wise rotation.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetRotationAngleCommand(double rotationAngle)
        {
            return new SetterCommand<double>(this, "RotationAngle", rotationAngle, UndoStack, RedoStack);
        }

        /// <summary>
        /// Creates an undoable command that sets the z-index of the section.
        /// </summary>
        /// <param name="zIndex">
        /// <para>
        /// Type: <see cref="Int32"/>
        /// </para>
        /// <para>
        /// The new z-index of the section.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetZIndexCommand(int zIndex)
        {
            return new SetterCommand<int>(this, "ZIndex", zIndex, UndoStack, RedoStack);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the background of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="System.Windows.Media.Brush"/>
        /// </para>
        /// <para>
        /// The brush that will paint the background of the section.
        /// </para>
        /// </value>
        public System.Windows.Media.Brush Background
        {
            get { return (System.Windows.Media.Brush)BrushConverter.Convert(((SectionElement)AdaptedElement).Background, typeof(System.Windows.Media.Brush), null, CultureInfo.CurrentCulture); }
            set { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets or sets the border brush of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="System.Windows.Media.Brush"/>
        /// </para>
        /// <para>
        /// The brush that will paint the border of the section.
        /// </para>
        /// </value>
        public System.Windows.Media.Brush BorderBrush
        {
            get { return (System.Windows.Media.Brush)BrushConverter.Convert(((SectionElement)AdaptedElement).BorderBrush, typeof(System.Windows.Media.Brush), null, CultureInfo.CurrentCulture); }
            set { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets or sets the thickness of the section border.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="System.Windows.Thickness"/>
        /// </para>
        /// <para>
        /// The thickness (in device independent pixels) of the section border.
        /// </para>
        /// </value>
        public System.Windows.Thickness BorderThickness
        {
            get { return (System.Windows.Thickness)StructuresConverter.Convert(((SectionElement)AdaptedElement).BorderThickness, typeof(System.Windows.Thickness), null, CultureInfo.CurrentCulture); }
            set { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets the interior of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="SectionInterior"/>
        /// </para>
        /// <para>
        /// The interior of the section.
        /// </para>
        /// </value>
        public SectionInterior Interior
        {
            get { return interior; }
        }

        /// <summary>
        /// Gets a value indicating whether a selection exists in the section content.
        /// </summary>
        /// <value>
        /// Indicates whether a selection exists in the section content.
        /// </value>
        public bool IsContentSelected
        {
            get { return Interior.IsContentSelected; }
        }

        /// <summary>
        /// Gets or sets the value indicating whether this section is currently selected or not.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Boolean"/>
        /// </para>
        /// <para>
        /// Indicates whether the section is currently selected on the <see cref="Page"/> or not.
        /// </para>
        /// </value>        
        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                isSelected = value;
                RaisePropertyChanged("IsSelected");
            }
        }

        /// <summary>
        /// Gets or sets the margin of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="System.Windows.Thickness"/>
        /// </para>
        /// <para>
        /// The thickness (in device independent pixels) of the section margin.
        /// </para>
        /// </value>
        public System.Windows.Thickness Margin
        {
            get { return (System.Windows.Thickness)StructuresConverter.Convert(((SectionElement)AdaptedElement).Margin, typeof(System.Windows.Thickness), null, CultureInfo.CurrentCulture); }
            set { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets or sets the angle of rotation transformation applied to this section.
        /// </summary>
        /// <value>
        /// <para>
        /// The angle, in degrees, of a clockwise rotation.
        /// </para>
        /// </value>
        public double RotationAngle
        {
            get 
            {
                double rotation;

                if (UnsavedChanges.ContainsKey("RotationAngle"))
                {
                    var cmd = (SetterCommand<double>)UnsavedChanges["RotationAngle"];
                    rotation = cmd.Value;
                }
                else
                    rotation = ((SectionElement)AdaptedElement).RotationAngle;

                return rotation; 
            }
            set
            {
                if (UnsavedChanges.ContainsKey("RotationAngle"))
                {
                    var cmd = (SetterCommand<double>)UnsavedChanges["RotationAngle"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["RotationAngle"] = CreateSetRotationAngleCommand(value);

                RaisePropertyChanged("RotationAngle");
            }
        }

        /// <summary>
        /// Gets or sets the z-index of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Int32"/>
        /// </para>
        /// <para>
        /// The z-index of the section.
        /// </para>
        /// </value>
        public int ZIndex
        {
            get { return GetPropertyValue<int>("ZIndex"); }
            set
            {
                if (UnsavedChanges.ContainsKey("ZIndex"))
                {
                    var cmd = (SetterCommand<int>)UnsavedChanges["ZIndex"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["ZIndex"] = CreateSetZIndexCommand(value);
            }
        }
        
        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="Interior"/> property.
        /// </summary>
        private SectionInterior interior;
        /// <summary>
        /// Holds the value of the <see cref="IsSelected"/> property.
        /// </summary>
        private bool isSelected;

        #endregion
    }
}
